suppressPackageStartupMessages({
library(Seurat)
library(Matrix)
library(ggplot2)
library(patchwork)
# remotes::install_github("chris-mcginnis-ucsf/DoubletFinder", upgrade = FALSE, dependencies = TRUE)
library(DoubletFinder)
library(scater)
})# url for source and intermediate data
path_data <- "https://export.uppmax.uu.se/naiss2023-23-3/workshops/workshop-scrnaseq"
path <- "./data/"
if (!dir.exists(path)) dir.create(path, recursive = T)
path_results <- "./results"
if (!dir.exists(path_results)) dir.create(path_results, recursive = T)
path_file <- file.path(path, "normal_pbmc_5.h5")
if (!file.exists(path_file)) {
download.file(url = file.path(file.path(path_data, "covid"), i), destfile = path_file)
}ctrl.5 <- Seurat::Read10X_h5(
filename = file.path(path, "normal_pbmc_5.h5"),
use.names = T
)
data <- CreateSeuratObject(ctrl.5, project = "ctrl_5")
# Check the seurat object
data
# How many genes do we have in the dataset?
# How many samples do we have in the dataset?
# Let's check the count matrix
data[["RNA"]]$counts[1:5, 1:5]
# What are the rows?
# What are the columns?## An object of class Seurat
## 33538 features across 1500 samples within 1 assay
## Active assay: RNA (33538 features, 0 variable features)
## 1 layer present: counts
## 5 x 5 sparse Matrix of class "dgCMatrix"
## AGGTCTAGTAGCTTAC-5 TACTTACAGCCTCGTG-5 CCGGTGAGTAGTGGCA-5
## MIR1302-2HG . . .
## FAM138A . . .
## OR4F5 . . .
## AL627309.1 . . .
## AL627309.3 . . .
## TCAGGGCTCTCCCTAG-5 CAAAGAAAGGAGGGTG-5
## MIR1302-2HG . .
## FAM138A . .
## OR4F5 . .
## AL627309.1 . .
## AL627309.3 . .
We can now start calculating some quality metrics. We can for example calculate the percentage of mitochondrial and ribosomal protein genes per cell and add to the metadata. The proportion of hemoglobin genes can give an indication of read blood cell contaminiation. The QC metrics are finally added to the metadata table.
# First let's check the metadata we have so far
data@meta.data[1:5, ]
# What does each column represent?
# Calcualte mitochondrial gene transcript percentage
data <- PercentageFeatureSet(data, "^MT-", col.name = "percent_mito")
# Percentage of ribosomal gene transcript percentage
data <- PercentageFeatureSet(data, "^RP[SL]", col.name = "percent_ribo")
# Percentage of hemoglobin genes - includes all genes starting with HB except HBP.
data <- PercentageFeatureSet(data, "^HB[^(P|E|S)]", col.name = "percent_hb")
# Percentage for some platelet markers
data <- PercentageFeatureSet(data, "PECAM1|PF4", col.name = "percent_plat")
# Let's check the metadata again
data@meta.data[1:5, ]feats <- c("nFeature_RNA", "nCount_RNA", "percent_mito", "percent_ribo", "percent_hb", "percent_plat")
VlnPlot(data, features = feats, pt.size = 0.1, ncol = 3)FeatureScatter(data, "nCount_RNA", "nFeature_RNA", pt.size = .5)
# What's the relationship between nCount_RNA and nFeature_RNA?
FeatureScatter(data, "nCount_RNA", "percent_mito", pt.size = .5)
# Do those cells with high percent_mito tend to have high or low nCount_RNA? What does that mean?
FeatureScatter(data, "nCount_RNA", "percent_ribo", pt.size = .5)
# Do those cells with low percent_ribo tend to have high or low nCount_RNA? What does this mean?
FeatureScatter(data, "nCount_RNA", "percent_hb", pt.size = .5)
FeatureScatter(data, "nCount_RNA", "percent_plat", pt.size = .5)
FeatureScatter(data, "percent_mito", "percent_ribo", pt.size = .5)
# Do those cells with low percent_ribo tend to have high or low percent_mito? What does this mean?A standard approach is to filter cells with low number of reads as well as genes that are present in at least a given number of cells. Here we will only consider cells with at least 200 detected genes and genes need to be expressed in at least 3 cells. Please note that those values are highly dependent on the library preparation method used.
# Check how many cells and genes to start with
dim(data)
selected_c <- WhichCells(data, expression = nFeature_RNA > 200)
selected_f <- rownames(data)[Matrix::rowSums(data) > 3]
data.filt <- subset(data, features = selected_f, cells = selected_c)
# Check how many cells and genes left?
dim(data.filt)
table(data.filt$orig.ident)## [1] 33538 1500
## [1] 33538 1371
##
## ctrl_5
## 1371
Additionally, we can also see which genes contribute the most to such reads, we can for instance plot the percentage of ocunts per gene
# Compute the proportion of counts of each gene per cell
# Use sparse matrix operations, if your dataset is large, doing matrix devisions the regular way will take a very long time.
C <- data.filt[["RNA"]]$counts
C@x <- C@x / rep.int(colSums(C), diff(C@p)) * 100
most_expressed <- order(Matrix::rowSums(C), decreasing = T)[20:1]
par(mar = c(2, 5, 2, 2)) # change margin size (bottom, left, top, right)
boxplot(as.matrix(t(C[most_expressed, ])),
cex = 0.1, las = 1, xlab = "Percent counts per cell",
col = (scales::hue_pal())(20)[20:1], horizontal = TRUE
)As you can see, MALAT1 continutes up to 30% of the UMIs from a single cell and the other top genes are mitochondrial genes and ribosomal genes.
From the FeatureScatter(data, “nCount_RNA”, “percent_mito”, pt.size = .5) generated plot above, we can see that we have quite a lot of cells with high proportion of mitochondrial and low nCount_RNA. It would be wise to remove those cells, if we have enough cells left after filtering. Another option would be to either remove all mitochondrial reads from the dataset and hope that the remaining genes still have enough biological signal. A third option would be to just regress out the percent_mito variable during scaling. In this case we had as much as 99.7% mitochondrial reads in some of the cells, so it is quite unlikely that there is much cell type signature left in those, it’s better to remove those cells.
We will obtain an adaptive threshold for the filtering, and assume that most of the dataset consists of high-quality cells. We then identify cells that are outliers for the various QC metrics, based on the median absolute deviation (MAD) from the median value of each metric across all cells. Specifically, a value is considered an outlier if it is more than 3 MADs from the median in the “problematic” direction. This is loosely motivated by the fact that such a filter will retain 99% of non-outlier values that follow a normal distribution.
outlier_mt = isOutlier(data.filt@meta.data$percent_mito, nmads=3, type="higher", log=F)
data.filt@meta.data$outlier_mt = as.vector(outlier_mt)
outlier_nCount_RNA = isOutlier(data.filt$nCount_RNA, nmads=3, type="lower", log=T)
data.filt@meta.data$outlier_nCount_RNA = as.vector(outlier_nCount_RNA)
# A cell that is an outlier for any of these metrics is considered to be of low quality and discarded.
data.filt@meta.data$Outlier_combined = data.filt@meta.data$outlier_mt | data.filt@meta.data$outlier_nCount_RNA
# Check the metadata table:
data.filt@meta.data[1:5, ]
# Now let's remove the identified outliers:
data.filt = subset(data.filt, Outlier_combined=="FALSE")
# Let's check how many cells do we have left now:
dim(data.filt)## [1] 33538 1022
Now let’s plot the QC metrics again:
feats <- c("nFeature_RNA", "nCount_RNA", "percent_mito", "percent_ribo", "percent_hb", "percent_plat")
VlnPlot(data.filt, features = feats, pt.size = 0.1, ncol = 3)FeatureScatter(data.filt, "nCount_RNA", "nFeature_RNA", pt.size = .5)
FeatureScatter(data.filt, "nCount_RNA", "percent_mito", pt.size = .5)
FeatureScatter(data.filt, "nCount_RNA", "percent_ribo", pt.size = .5)
FeatureScatter(data.filt, "nCount_RNA", "percent_hb", pt.size = .5)
FeatureScatter(data.filt, "nCount_RNA", "percent_plat", pt.size = .5)
FeatureScatter(data.filt, "percent_mito", "percent_ribo", pt.size = .5)Compare the plots to those above, do you think the filtering works?
dim(data.filt)
# Filter MALAT1
data.filt <- data.filt[!grepl("MALAT1", rownames(data.filt)), ]
# Filter Mitocondrial
data.filt <- data.filt[!grepl("^MT-", rownames(data.filt)), ]
# Filter Ribossomal gene (optional if that is a problem on your data)
# data.filt <- data.filt[ ! grepl("^RP[SL]", rownames(data.filt)), ]
# Filter Hemoglobin gene (optional if that is a problem on your data)
data.filt <- data.filt[!grepl("^HB[^(P|E|S)]", rownames(data.filt)), ]
dim(data.filt)## [1] 33538 1022
## [1] 33515 1022
# Before running CellCycleScoring the data need to be normalized and logtransformed.
data.filt <- NormalizeData(data.filt)
data.filt <- CellCycleScoring(
object = data.filt,
g2m.features = cc.genes$g2m.genes,
s.features = cc.genes$s.genes
)VlnPlot(data.filt, features = c("S.Score", "G2M.Score"), group.by = "orig.ident", ncol = 3, pt.size = .1)FeatureScatter(data.filt, "S.Score", "G2M.Score", group.by = "Phase")Seurat does an automatic prediction of cell cycle phase with a default cutoff of the scores at zero. As you can see this does not fit this data very well (think about it, S.Score and G2M.Score close to 0), so be cautious with using these predictions. Instead we suggest that you look at the scores.
Here, we will use DoubletFinder to predict doublet
cells. But before doing doublet detection we need to run scaling,
variable gene selection and PCA, as well as UMAP for visualization.
These steps will be explored in more detail in coming exercises.
data.filt <- FindVariableFeatures(data.filt, verbose = F)
data.filt <- ScaleData(data.filt, vars.to.regress = c("nFeature_RNA", "percent_mito"), verbose = F)
data.filt <- RunPCA(data.filt, verbose = F, npcs = 20)
data.filt <- RunUMAP(data.filt, dims = 1:10, verbose = F)Then we run doubletFinder, selecting first 10 PCs and a
pK value of 0.9. To optimize the parameters, you can run
the paramSweep function in the package.
# set seed
set.seed(8)
#ref: https://uofuhealth.utah.edu/huntsman/shared-resources/gcb/htg/single-cell/genomics-10x
cells.nr = ncol(data.filt)
nExp.pois = if (cells.nr < 750) {
round(cells.nr * 0.04)
} else if (cells.nr < 1500) {
round(cells.nr * 0.008)
} else if (cells.nr < 2500) {
round(cells.nr * 0.016)
} else if (cells.nr < 3500) {
round(cells.nr * 0.023)
} else if (cells.nr < 4500) {
round(cells.nr * 0.031)
} else if (cells.nr < 5500) {
round(cells.nr * 0.039)
} else if (cells.nr < 6500) {
round(cells.nr * 0.046)
} else if (cells.nr < 7500) {
round(cells.nr * 0.054)
} else if (cells.nr < 8500) {
round(cells.nr * 0.061)
} else if (cells.nr < 9500) {
round(cells.nr * 0.069)
} else {
round(cells.nr * 0.076)
}
nExp.pois
# Find significant PCs
stdv <- data.filt[["pca"]]@stdev
sum.stdv <- sum(data.filt[["pca"]]@stdev)
percent.stdv <- (stdv / sum.stdv) * 100
cumulative <- cumsum(percent.stdv)
co1 <- which(cumulative > 90 & percent.stdv < 5)[1]
co2 <- sort(which((percent.stdv[1:length(percent.stdv) - 1] -
percent.stdv[2:length(percent.stdv)]) > 0.1),
decreasing = T)[1] + 1
min.pc <- min(co1, co2)
min.pc
# pK identification (no ground-truth)
sweep.list <- paramSweep(data.filt, PCs = 1:min.pc, num.cores = 1) #detectCores() - 1)
sweep.stats <- summarizeSweep(sweep.list)
bcmvn <- find.pK(sweep.stats)
# Optimal pK is the max of the bimodality coefficent (BCmvn) distribution
bcmvn.max <- bcmvn[which.max(bcmvn$BCmetric),]
optimal.pk <- bcmvn.max$pK
optimal.pk <- as.numeric(levels(optimal.pk))[optimal.pk]
optimal.pk
# run DoubletFinder
data.filt <- doubletFinder(seu = data.filt,
PCs = 1:min.pc,
pK = optimal.pk,
nExp = nExp.pois)
metadata <- data.filt@meta.data
colnames(metadata)[ncol(metadata)] <- "doublet_finder"
data.filt@meta.data <- metadata
head(data.filt@meta.data)
table(data.filt@meta.data$doublet_finder)## [1] 8
## [1] 12
## [1] "Creating artificial doublets for pN = 5%"
## [1] "Creating Seurat object..."
## [1] "Normalizing Seurat object..."
## [1] "Finding variable genes..."
## [1] "Scaling data..."
## [1] "Running PCA..."
## [1] "Calculating PC distance matrix..."
## [1] "Defining neighborhoods..."
## [1] "Computing pANN across all pK..."
## [1] "pK = 0.01..."
## [1] "pK = 0.02..."
## [1] "pK = 0.03..."
## [1] "pK = 0.04..."
## [1] "pK = 0.05..."
## [1] "pK = 0.06..."
## [1] "pK = 0.07..."
## [1] "pK = 0.08..."
## [1] "pK = 0.09..."
## [1] "pK = 0.1..."
## [1] "pK = 0.11..."
## [1] "pK = 0.12..."
## [1] "pK = 0.13..."
## [1] "pK = 0.14..."
## [1] "pK = 0.15..."
## [1] "pK = 0.16..."
## [1] "pK = 0.17..."
## [1] "pK = 0.18..."
## [1] "pK = 0.19..."
## [1] "pK = 0.2..."
## [1] "pK = 0.21..."
## [1] "pK = 0.22..."
## [1] "pK = 0.23..."
## [1] "pK = 0.24..."
## [1] "pK = 0.25..."
## [1] "pK = 0.26..."
## [1] "pK = 0.27..."
## [1] "pK = 0.28..."
## [1] "pK = 0.29..."
## [1] "pK = 0.3..."
## [1] "Creating artificial doublets for pN = 10%"
## [1] "Creating Seurat object..."
## [1] "Normalizing Seurat object..."
## [1] "Finding variable genes..."
## [1] "Scaling data..."
## [1] "Running PCA..."
## [1] "Calculating PC distance matrix..."
## [1] "Defining neighborhoods..."
## [1] "Computing pANN across all pK..."
## [1] "pK = 0.01..."
## [1] "pK = 0.02..."
## [1] "pK = 0.03..."
## [1] "pK = 0.04..."
## [1] "pK = 0.05..."
## [1] "pK = 0.06..."
## [1] "pK = 0.07..."
## [1] "pK = 0.08..."
## [1] "pK = 0.09..."
## [1] "pK = 0.1..."
## [1] "pK = 0.11..."
## [1] "pK = 0.12..."
## [1] "pK = 0.13..."
## [1] "pK = 0.14..."
## [1] "pK = 0.15..."
## [1] "pK = 0.16..."
## [1] "pK = 0.17..."
## [1] "pK = 0.18..."
## [1] "pK = 0.19..."
## [1] "pK = 0.2..."
## [1] "pK = 0.21..."
## [1] "pK = 0.22..."
## [1] "pK = 0.23..."
## [1] "pK = 0.24..."
## [1] "pK = 0.25..."
## [1] "pK = 0.26..."
## [1] "pK = 0.27..."
## [1] "pK = 0.28..."
## [1] "pK = 0.29..."
## [1] "pK = 0.3..."
## [1] "Creating artificial doublets for pN = 15%"
## [1] "Creating Seurat object..."
## [1] "Normalizing Seurat object..."
## [1] "Finding variable genes..."
## [1] "Scaling data..."
## [1] "Running PCA..."
## [1] "Calculating PC distance matrix..."
## [1] "Defining neighborhoods..."
## [1] "Computing pANN across all pK..."
## [1] "pK = 0.01..."
## [1] "pK = 0.02..."
## [1] "pK = 0.03..."
## [1] "pK = 0.04..."
## [1] "pK = 0.05..."
## [1] "pK = 0.06..."
## [1] "pK = 0.07..."
## [1] "pK = 0.08..."
## [1] "pK = 0.09..."
## [1] "pK = 0.1..."
## [1] "pK = 0.11..."
## [1] "pK = 0.12..."
## [1] "pK = 0.13..."
## [1] "pK = 0.14..."
## [1] "pK = 0.15..."
## [1] "pK = 0.16..."
## [1] "pK = 0.17..."
## [1] "pK = 0.18..."
## [1] "pK = 0.19..."
## [1] "pK = 0.2..."
## [1] "pK = 0.21..."
## [1] "pK = 0.22..."
## [1] "pK = 0.23..."
## [1] "pK = 0.24..."
## [1] "pK = 0.25..."
## [1] "pK = 0.26..."
## [1] "pK = 0.27..."
## [1] "pK = 0.28..."
## [1] "pK = 0.29..."
## [1] "pK = 0.3..."
## [1] "Creating artificial doublets for pN = 20%"
## [1] "Creating Seurat object..."
## [1] "Normalizing Seurat object..."
## [1] "Finding variable genes..."
## [1] "Scaling data..."
## [1] "Running PCA..."
## [1] "Calculating PC distance matrix..."
## [1] "Defining neighborhoods..."
## [1] "Computing pANN across all pK..."
## [1] "pK = 0.01..."
## [1] "pK = 0.02..."
## [1] "pK = 0.03..."
## [1] "pK = 0.04..."
## [1] "pK = 0.05..."
## [1] "pK = 0.06..."
## [1] "pK = 0.07..."
## [1] "pK = 0.08..."
## [1] "pK = 0.09..."
## [1] "pK = 0.1..."
## [1] "pK = 0.11..."
## [1] "pK = 0.12..."
## [1] "pK = 0.13..."
## [1] "pK = 0.14..."
## [1] "pK = 0.15..."
## [1] "pK = 0.16..."
## [1] "pK = 0.17..."
## [1] "pK = 0.18..."
## [1] "pK = 0.19..."
## [1] "pK = 0.2..."
## [1] "pK = 0.21..."
## [1] "pK = 0.22..."
## [1] "pK = 0.23..."
## [1] "pK = 0.24..."
## [1] "pK = 0.25..."
## [1] "pK = 0.26..."
## [1] "pK = 0.27..."
## [1] "pK = 0.28..."
## [1] "pK = 0.29..."
## [1] "pK = 0.3..."
## [1] "Creating artificial doublets for pN = 25%"
## [1] "Creating Seurat object..."
## [1] "Normalizing Seurat object..."
## [1] "Finding variable genes..."
## [1] "Scaling data..."
## [1] "Running PCA..."
## [1] "Calculating PC distance matrix..."
## [1] "Defining neighborhoods..."
## [1] "Computing pANN across all pK..."
## [1] "pK = 0.01..."
## [1] "pK = 0.02..."
## [1] "pK = 0.03..."
## [1] "pK = 0.04..."
## [1] "pK = 0.05..."
## [1] "pK = 0.06..."
## [1] "pK = 0.07..."
## [1] "pK = 0.08..."
## [1] "pK = 0.09..."
## [1] "pK = 0.1..."
## [1] "pK = 0.11..."
## [1] "pK = 0.12..."
## [1] "pK = 0.13..."
## [1] "pK = 0.14..."
## [1] "pK = 0.15..."
## [1] "pK = 0.16..."
## [1] "pK = 0.17..."
## [1] "pK = 0.18..."
## [1] "pK = 0.19..."
## [1] "pK = 0.2..."
## [1] "pK = 0.21..."
## [1] "pK = 0.22..."
## [1] "pK = 0.23..."
## [1] "pK = 0.24..."
## [1] "pK = 0.25..."
## [1] "pK = 0.26..."
## [1] "pK = 0.27..."
## [1] "pK = 0.28..."
## [1] "pK = 0.29..."
## [1] "pK = 0.3..."
## [1] "Creating artificial doublets for pN = 30%"
## [1] "Creating Seurat object..."
## [1] "Normalizing Seurat object..."
## [1] "Finding variable genes..."
## [1] "Scaling data..."
## [1] "Running PCA..."
## [1] "Calculating PC distance matrix..."
## [1] "Defining neighborhoods..."
## [1] "Computing pANN across all pK..."
## [1] "pK = 0.01..."
## [1] "pK = 0.02..."
## [1] "pK = 0.03..."
## [1] "pK = 0.04..."
## [1] "pK = 0.05..."
## [1] "pK = 0.06..."
## [1] "pK = 0.07..."
## [1] "pK = 0.08..."
## [1] "pK = 0.09..."
## [1] "pK = 0.1..."
## [1] "pK = 0.11..."
## [1] "pK = 0.12..."
## [1] "pK = 0.13..."
## [1] "pK = 0.14..."
## [1] "pK = 0.15..."
## [1] "pK = 0.16..."
## [1] "pK = 0.17..."
## [1] "pK = 0.18..."
## [1] "pK = 0.19..."
## [1] "pK = 0.2..."
## [1] "pK = 0.21..."
## [1] "pK = 0.22..."
## [1] "pK = 0.23..."
## [1] "pK = 0.24..."
## [1] "pK = 0.25..."
## [1] "pK = 0.26..."
## [1] "pK = 0.27..."
## [1] "pK = 0.28..."
## [1] "pK = 0.29..."
## [1] "pK = 0.3..."
## NULL
## [1] 0.12
## [1] "Creating 341 artificial doublets..."
## [1] "Creating Seurat object..."
## [1] "Normalizing Seurat object..."
## [1] "Finding variable genes..."
## [1] "Scaling data..."
## [1] "Running PCA..."
## [1] "Calculating PC distance matrix..."
## [1] "Computing pANN..."
## [1] "Classifying doublets.."
##
## Doublet Singlet
## 8 1014
saveRDS(data.filt, "results/data.filt.RDS")We did data filetring, normalization and cell cycle scoring in this tutorial
sessionInfo()## R version 4.2.2 (2022-10-31)
## Platform: x86_64-apple-darwin13.4.0 (64-bit)
## Running under: macOS Big Sur ... 10.16
##
## Matrix products: default
## BLAS/LAPACK: /Users/ekol-yal/miniconda3/envs/seurat5/lib/libopenblasp-r0.3.21.dylib
##
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## attached base packages:
## [1] parallel stats4 stats graphics grDevices utils datasets
## [8] methods base
##
## other attached packages:
## [1] ROCR_1.0-11 KernSmooth_2.23-20
## [3] fields_16.2 viridisLite_0.4.1
## [5] spam_2.10-0 scater_1.26.1
## [7] scuttle_1.8.4 SingleCellExperiment_1.20.1
## [9] SummarizedExperiment_1.28.0 Biobase_2.58.0
## [11] GenomicRanges_1.50.2 GenomeInfoDb_1.34.9
## [13] IRanges_2.32.0 S4Vectors_0.36.2
## [15] BiocGenerics_0.44.0 MatrixGenerics_1.10.0
## [17] matrixStats_0.63.0 DoubletFinder_2.0.4
## [19] patchwork_1.1.2 ggplot2_3.4.1
## [21] Matrix_1.6-5 Seurat_5.0.1
## [23] SeuratObject_5.0.1 sp_1.6-0
## [25] captioner_2.2.3 bookdown_0.33
## [27] knitr_1.42
##
## loaded via a namespace (and not attached):
## [1] utf8_1.2.3 spatstat.explore_3.1-0
## [3] reticulate_1.28 tidyselect_1.2.0
## [5] htmlwidgets_1.6.1 grid_4.2.2
## [7] BiocParallel_1.32.5 Rtsne_0.16
## [9] munsell_0.5.0 ScaledMatrix_1.6.0
## [11] codetools_0.2-19 ica_1.0-3
## [13] future_1.31.0 miniUI_0.1.1.1
## [15] withr_2.5.0 spatstat.random_3.1-4
## [17] colorspace_2.1-0 progressr_0.13.0
## [19] highr_0.10 rstudioapi_0.14
## [21] tensor_1.5 listenv_0.9.0
## [23] labeling_0.4.2 GenomeInfoDbData_1.2.9
## [25] polyclip_1.10-4 bit64_4.0.5
## [27] farver_2.1.1 parallelly_1.34.0
## [29] vctrs_0.5.2 generics_0.1.3
## [31] xfun_0.37 R6_2.5.1
## [33] ggbeeswarm_0.7.2 rsvd_1.0.5
## [35] hdf5r_1.3.8 bitops_1.0-7
## [37] spatstat.utils_3.0-2 cachem_1.0.7
## [39] DelayedArray_0.24.0 promises_1.2.0.1
## [41] scales_1.2.1 beeswarm_0.4.0
## [43] gtable_0.3.1 beachmat_2.14.2
## [45] globals_0.16.2 goftest_1.2-3
## [47] rlang_1.0.6 splines_4.2.2
## [49] lazyeval_0.2.2 spatstat.geom_3.1-0
## [51] yaml_2.3.7 reshape2_1.4.4
## [53] abind_1.4-5 httpuv_1.6.9
## [55] tools_4.2.2 ellipsis_0.3.2
## [57] jquerylib_0.1.4 RColorBrewer_1.1-3
## [59] ggridges_0.5.4 Rcpp_1.0.10
## [61] plyr_1.8.8 sparseMatrixStats_1.10.0
## [63] zlibbioc_1.44.0 purrr_1.0.1
## [65] RCurl_1.98-1.10 deldir_1.0-6
## [67] pbapply_1.7-0 viridis_0.6.2
## [69] cowplot_1.1.1 zoo_1.8-11
## [71] ggrepel_0.9.3 cluster_2.1.4
## [73] magrittr_2.0.3 data.table_1.14.8
## [75] RSpectra_0.16-1 scattermore_1.2
## [77] lmtest_0.9-40 RANN_2.6.1
## [79] fitdistrplus_1.1-11 mime_0.12
## [81] evaluate_0.20 xtable_1.8-4
## [83] fastDummies_1.7.3 gridExtra_2.3
## [85] compiler_4.2.2 tibble_3.1.8
## [87] maps_3.4.1 crayon_1.5.2
## [89] htmltools_0.5.4 later_1.3.0
## [91] tidyr_1.3.0 MASS_7.3-58.2
## [93] cli_3.6.0 dotCall64_1.1-1
## [95] igraph_1.4.1 pkgconfig_2.0.3
## [97] plotly_4.10.1 spatstat.sparse_3.0-1
## [99] vipor_0.4.7 bslib_0.4.2
## [101] XVector_0.38.0 stringr_1.5.0
## [103] digest_0.6.31 sctransform_0.4.1
## [105] RcppAnnoy_0.0.20 spatstat.data_3.0-1
## [107] rmarkdown_2.20 leiden_0.4.3
## [109] uwot_0.1.14 DelayedMatrixStats_1.20.0
## [111] shiny_1.7.4 lifecycle_1.0.3
## [113] nlme_3.1-162 jsonlite_1.8.4
## [115] BiocNeighbors_1.16.0 fansi_1.0.4
## [117] pillar_1.8.1 lattice_0.20-45
## [119] ggrastr_1.0.2 fastmap_1.1.1
## [121] httr_1.4.5 survival_3.5-3
## [123] glue_1.6.2 png_0.1-8
## [125] bit_4.0.5 stringi_1.7.12
## [127] sass_0.4.5 RcppHNSW_0.4.1
## [129] BiocSingular_1.14.0 dplyr_1.1.0
## [131] irlba_2.3.5.1 future.apply_1.10.0